//你好
const { def, base, intField, setEvent, regFieldGetSet, regEvent } = require("./jsdemo_windows.common.cpp.js");

const lib3d = String.raw/* cpp */`

class Object3d{
    public:
    Object3d();
    ~Object3d();
    ObjData* objData;
    JSValue js_obj;

  };
  class Canvas3d{
    public:
    Canvas3d(int width, int height);
    ~Canvas3d();

    int width;
    int height;
    Projection* projection;
    Camera* cam;
    ArrayList* objs;//<ObjData>
    Matrix* camp;
    Matrix* tcamp;
    void* frameBuffer;

    int faceCount;
    Face* faces;
    Face** faceIndex;

    int objCount;
    ObjData** objArr;

    JSValue js_obj;
    void* render();
  };
  Canvas3d::Canvas3d(int width, int height){
    projection = new Projection(640, 480);

    cam = new Camera();

    objs = new ArrayList();

    camp = new Matrix(4);

    tcamp = new Matrix(4);

    this->width = width;
    this->height = height;
    frameBuffer = (void*)malloca(width*height*2 + 2048, 2048);

    objCount = 0;
    objArr = 0;

    faceCount = 0;
    faces = 0;
    faceIndex = 0;

  }

  Canvas3d::~Canvas3d(){
    printf("~Canvas3d\n");

    for(int i = 0; i< objs->length;i++){
      Object3d* object3d = (Object3d*)objs->get(i);
      JS_FreeValue(ctx, object3d->js_obj);
    }
    delete projection;
    delete cam;
    delete objs;//<ObjData>
    delete camp;
    delete tcamp;
    free(frameBuffer);
    delete faces;
    delete faceIndex;
    delete objArr;
  }
  void* Canvas3d::render(){

    int newFaceCount = 0;
    int newObjCount = objs->length;

    if(newObjCount!=objCount){
      if(objArr){
        delete objArr;
      }
      objCount = newObjCount;
      objArr = (ObjData**)malloc(4 * objCount);
    }

    for (int k = 0; k < objs->length; k++) {
      Object3d* obj = (Object3d*)objs->get(k);
      objArr[k] = obj->objData;
      newFaceCount += obj->objData->faceCount;
    }

    if(newFaceCount!=faceCount){
      if(faces){
        delete faces;
      }
      if(faceIndex){
        delete faceIndex;
      }
      faceCount = newFaceCount;
      faces = (Face*)malloc(sizeof(Face) * faceCount);
      faceIndex = (Face**)malloc(4 * faceCount);
    }


    {
      IOWR(VGA_BLT, BLT_S_SWITCH, SWITCH_SCREEN_MEM_03 | 1);// 0: screen 1:mem
      paint_pattern(0, ((int)frameBuffer) >> 11, 1024, (width*height)>>10, 0x000F, 0);
      render3D(projection, cam, objArr, objCount, faces, faceIndex, frameBuffer, camp, tcamp);
      IOWR(VGA_BLT, BLT_S_SWITCH, SWITCH_SCREEN_MEM_03 | 0);// 0: screen 1:mem
    }
    return frameBuffer;
  }


  Object3d::Object3d(){

  }

  Object3d::~Object3d(){
    printf("~Object3d\n");
    delete objData;
  }




  static JSValue js_canvas3d_ctor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv) {
    if(argc==2){
      int width;
      JS_ToInt32(ctx, &width, argv[0]);
      int height;
      JS_ToInt32(ctx, &height, argv[1]);

//TODO check 640 480

      Canvas3d* canvas3d = new Canvas3d(width, height);
      JSValue obj = JS_NewObjectClass(ctx, js_canvas3d_class_id);
      JS_SetOpaque(obj, canvas3d);
      canvas3d->js_obj = obj;
      return obj;
    }else{
      return JS_UNDEFINED;
    }
  }
  static void js_canvas3d_finalizer(JSRuntime *rt, JSValue val) {
    Canvas3d *canvas3d = (Canvas3d*)JS_GetOpaque(val);
    if(canvas3d){
      delete canvas3d;
    }
  }


  static JSValue js_canvas3d_addObj(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
    Canvas3d *canvas3d = (Canvas3d*)JS_GetOpaque(this_val);
    if (!JS_IsInstanceOf(ctx, argv[0], JS_GetPropertyStr(ctx, global, "Object3d"))) {
      return JS_UNDEFINED;
    }
    Object3d *object3d = (Object3d*)JS_GetOpaque(argv[0]);
    if(canvas3d && object3d){
      JS_DupValue(ctx, argv[0]);
      canvas3d->objs->push(object3d);
    }
    return JS_UNDEFINED;
  }

  static JSValue js_canvas3d_removeObj(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
    Canvas3d *canvas3d = (Canvas3d*)JS_GetOpaque(this_val);
    if (!JS_IsInstanceOf(ctx, argv[0], JS_GetPropertyStr(ctx, global, "Object3d"))) {
      return JS_UNDEFINED;
    }
    Object3d *object3d = (Object3d*)JS_GetOpaque(argv[0]);
    JS_FreeValue(ctx, argv[0]);
    canvas3d->objs->remove(object3d);
    return JS_UNDEFINED;
  }


  static JSValue js_canvas3d_render(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
    Canvas3d *canvas3d = (Canvas3d*)JS_GetOpaque(this_val);

    void* frameBuffer = canvas3d->render();

    int byte_length = canvas3d->width * canvas3d->height * 2;
    JSValue buffer = JS_NewArrayBuffer(ctx, (uint8_t*)frameBuffer, byte_length, 0, 0, 0);
    JSValue ret = JS_NewObject(ctx);
    JS_SetPropertyStr(ctx, ret, "buffer", buffer);
    JS_SetPropertyStr(ctx, ret, "width", JS_NewInt32(ctx, canvas3d->width));
    JS_SetPropertyStr(ctx, ret, "height", JS_NewInt32(ctx, canvas3d->height));
  
    return ret;
  }



  static JSValue js_object3d_ctor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv) {
    if(argc==2){
      size_t size1 = 0;
      void* buff1 = getBuffer(argv[0], &size1);
      size_t size2 = 0;
      void* buff2 = getBuffer(argv[1], &size2);

      if(size1 <= 0){
        return JS_UNDEFINED;
      }
      if(size2 != 256*256*2){
        return JS_UNDEFINED;
      }
      
      Object3d* object3d = new Object3d();
      object3d->objData = new ObjData(buff1, buff2);
      object3d->objData->show = true;

      JSValue obj = JS_NewObjectClass(ctx, js_object3d_class_id);
      JS_SetOpaque(obj, object3d);
      object3d->js_obj = obj;
      return obj;
    }else{
      return JS_UNDEFINED;
    }
  }
  static void js_object3d_finalizer(JSRuntime *rt, JSValue val) {
    Object3d *p = (Object3d*)JS_GetOpaque(val);
    if(p){
      delete p;
    }
  }


  static JSValue js_object3d_resetMatrix(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
    Object3d* object3d = (Object3d*)JS_GetOpaque(this_val);
    object3d->objData->matrix->init();
    return JS_UNDEFINED;
  }
  static JSValue js_object3d_rotateX(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
    Object3d* object3d = (Object3d*)JS_GetOpaque(this_val);
    if(argc==1){
      float val = 0;
      JS_ToFloat64(ctx, &val, argv[0]);
      object3d->objData->matrix->rotateX(val);
    }
    return JS_UNDEFINED;
  }
  static JSValue js_object3d_rotateY(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
    Object3d* object3d = (Object3d*)JS_GetOpaque(this_val);
    if(argc==1){
      float val = 0;
      JS_ToFloat64(ctx, &val, argv[0]);
      object3d->objData->matrix->rotateY(val);
    }
    return JS_UNDEFINED;
  }
  static JSValue js_object3d_rotateZ(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
    Object3d* object3d = (Object3d*)JS_GetOpaque(this_val);
    if(argc==1){
      float val = 0;
      JS_ToFloat64(ctx, &val, argv[0]);
      object3d->objData->matrix->rotateZ(val);
    }
    return JS_UNDEFINED;
  }
  static JSValue js_object3d_move(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
    Object3d* object3d = (Object3d*)JS_GetOpaque(this_val);
    if(argc==3){
      float val0 = 0; JS_ToFloat64(ctx, &val0, argv[0]);
      float val1 = 0; JS_ToFloat64(ctx, &val1, argv[1]);
      float val2 = 0; JS_ToFloat64(ctx, &val2, argv[2]);
      object3d->objData->matrix->move(val0, val1, val2);
    }
    return JS_UNDEFINED;
  }
  static JSValue js_object3d_scale(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
    Object3d* object3d = (Object3d*)JS_GetOpaque(this_val);
    if(argc==3){
      float val0 = 0; JS_ToFloat64(ctx, &val0, argv[0]);
      float val1 = 0; JS_ToFloat64(ctx, &val1, argv[1]);
      float val2 = 0; JS_ToFloat64(ctx, &val2, argv[2]);
      object3d->objData->matrix->scale(val0, val1, val2);
    }
    return JS_UNDEFINED;
  }




  void lib3d_init() {
    staticMatrixInit();

    {
      JS_NewClassID(&js_canvas3d_class_id);
      JSClassDef canvas3d_def = {
        .class_name = "Canvas3d",
        .finalizer = js_canvas3d_finalizer,
      };
      JS_NewClass(rt, js_canvas3d_class_id, &canvas3d_def);

      JSValue canvas3d_proto = JS_NewObject(ctx);

      JS_SetClassProto(ctx, js_canvas3d_class_id, canvas3d_proto);

      JS_SetPropertyStr(ctx, canvas3d_proto, "addObj", JS_NewCFunction(ctx, js_canvas3d_addObj, "addObj", 1));
      JS_SetPropertyStr(ctx, canvas3d_proto, "removeObj", JS_NewCFunction(ctx, js_canvas3d_removeObj, "removeObj", 1));
      JS_SetPropertyStr(ctx, canvas3d_proto, "render", JS_NewCFunction(ctx, js_canvas3d_render, "render", 0));

      JSValue canvas3d_ctor = JS_NewCFunction2(ctx, js_canvas3d_ctor, "Canvas3d", 2, JS_CFUNC_constructor, 2);
      JS_SetConstructor(ctx, canvas3d_ctor, canvas3d_proto);
      JS_SetPropertyStr(ctx, global, "Canvas3d", canvas3d_ctor);
    }

    {
      JS_NewClassID(&js_object3d_class_id);
      JSClassDef object3d_def = {
        .class_name = "Object3d",
        .finalizer = js_object3d_finalizer,
      };
      JS_NewClass(rt, js_object3d_class_id, &object3d_def);

      JSValue object3d_proto = JS_NewObject(ctx);

      JS_SetClassProto(ctx, js_object3d_class_id, object3d_proto);
     

      JS_SetPropertyStr(ctx, object3d_proto, "resetMatrix", JS_NewCFunction(ctx, js_object3d_resetMatrix , "resetMatrix", 1));
      JS_SetPropertyStr(ctx, object3d_proto, "rotateX", JS_NewCFunction(ctx, js_object3d_rotateX , "rotateX", 1));
      JS_SetPropertyStr(ctx, object3d_proto, "rotateY", JS_NewCFunction(ctx, js_object3d_rotateY , "rotateY", 1));
      JS_SetPropertyStr(ctx, object3d_proto, "rotateZ", JS_NewCFunction(ctx, js_object3d_rotateZ , "rotateZ", 3));
      JS_SetPropertyStr(ctx, object3d_proto, "move", JS_NewCFunction(ctx, js_object3d_move , "move", 3));
      JS_SetPropertyStr(ctx, object3d_proto, "scale", JS_NewCFunction(ctx, js_object3d_scale , "scale", 3));

      JSValue object3d_ctor = JS_NewCFunction2(ctx, js_object3d_ctor, "Object3d", 2, JS_CFUNC_constructor, 2);
      JS_SetConstructor(ctx, object3d_ctor, object3d_proto);
      JS_SetPropertyStr(ctx, global, "Object3d", object3d_ctor);
    }

  }




`;

module.exports = { lib3d };





    //JS_SetPropertyStr(ctx, global, "test3d", JS_NewCFunction(ctx, test3d, "test3d", 10));

//canvas new Canvas3D(width, height)
//new Object3D(Object, Texture)

    //obj.scale(1.0f, 1.0f, 1.0f);
    //obj.move(pos, 2.1f, 2.1f);
    //obj.rotateY(rot);
    //obj.show();
    //obj.hidden();
    //canvas.render() 返回 buffer，
    //再paint到窗体



  // void rotateX(float a);
  // void rotateY(float a);
  // void rotateZ(float a);
  // void move(float x, float y, float z);
  // void scale(float x, float y, float z);





//   Camera* cam;
//   Projection* projection;
//   ArrayList* objs;
//   void* frameBuffer;

// static JSValue test3d(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
//   printf("test3d\n");

//   int match1 = JS_IsInstanceOf(ctx, argv[0], JS_GetPropertyStr(ctx, global, "ArrayBuffer"));
//   printf("match1: %d\n", match1);
  
//   JSValueConst v1 = JS_GetPropertyStr(ctx, global, "fdsa");
//   JSValueConst v2 = JS_GetPropertyStr(ctx, global, "ArrayBuffer");//TypedArray
//   JSValueConst v3 = JS_GetPropertyStr(ctx, global, "Uint16Array");
//   JSClassID cid = JS_GetClassID(argv[0]); 
//   printf("v1 %d\n", v1.tag);
//   printf("v2 %d\n", v2.tag);
//   printf("v3 %d\n", v3.tag);
//   printf("cid %d\n", cid);
//   printf("a %d\n", JS_CLASS_UINT8C_ARRAY);
//   printf("b %d\n", JS_CLASS_FLOAT32_ARRAY);
//   printf("c %d\n", JS_CLASS_ARRAY_BUFFER);

//   // JS_CLASS_UINT8C_ARRAY,      /* u.array (typed_array) */
//   // JS_CLASS_INT8_ARRAY,        /* u.array (typed_array) */
//   // JS_CLASS_UINT8_ARRAY,       /* u.array (typed_array) */
//   // JS_CLASS_INT16_ARRAY,       /* u.array (typed_array) */
//   // JS_CLASS_UINT16_ARRAY,      /* u.array (typed_array) */
//   // JS_CLASS_INT32_ARRAY,       /* u.array (typed_array) */
//   // JS_CLASS_UINT32_ARRAY,      /* u.array (typed_array) */
//   // JS_CLASS_FLOAT32_ARRAY,     /* u.array (typed_array) */

//   int match2 = JS_IsInstanceOf(ctx, argv[1], v2);//Uint16Array
//   printf("match2: %d\n", match2);

//   while(1);
  
//   Canvas3d* canvas = new Canvas3d(640, 480);

//   void* cubeDat   = loadFile("/3dobj/cube.b3o"  );
//   void* aaaDat    = loadFile("/3dobj/aaa.b3o"   );
//   void* sphereDat = loadFile("/3dobj/sphere.b3o");

//   printf("loaded1\n");
//   int ww;
//   int hh;
//   void* cubeTex   = loadImg("/3dobj/cube.jpg"  , &ww, &hh);
//   void* aaaTex    = loadImg("/3dobj/aaa.jpg"   , &ww, &hh);
//   void* sphereTex = loadImg("/3dobj/sphere.jpg", &ww, &hh);  

//   printf("loaded2\n");

//   ObjData* cube   = new ObjData();
//   ObjData* aaa    = new ObjData();
//   ObjData* sphere = new ObjData();

//   cube  ->load(cubeDat  );
//   aaa   ->load(aaaDat   );
//   sphere->load(sphereDat);

//   setTexture(cube  , cubeTex  );
//   setTexture(aaa   , aaaTex   );
//   setTexture(sphere, sphereTex);
  
//   cube  ->show = true;
//   aaa   ->show = true;
//   sphere->show = true;

//   //Object3d* cube   = new Object3d(cubeDat  , cubeTex  );
//   //Object3d* aaa    = new Object3d(aaaDat   , aaaTex   );
//   //Object3d* sphere = new Object3d(sphereDat, sphereTex);

//   canvas->objs->push(cube);
//   canvas->objs->push(aaa);
//   canvas->objs->push(sphere);

//   printf("loaded3\n");
//   float pos;
//   float rot;
//   float rot2;
//   pos = 0;
//   rot = 0;
//   rot2 = 0;

//   pos = pos + 0.5f;
//   //if (pos > 50) pos = -50;

//   rot2 = rot2 + 0.03f;
//   //if (rot2 > 3.1416*2) rot2 = 0;

//   cube->matrix->init();
//   cube->matrix->scale(1.0f, 1.0f, 1.0f);
//   cube->matrix->move(pos, 2.1f, 2.1f);
  
//   aaa->matrix->init();
//   aaa->matrix->scale(0.2f, 0.2f, 0.2f);
//   //aaa.matrix.scale(0.5f, 0.5f, 0.5f);
//   //aaa.matrix.rotateY(rot);

//   sphere->matrix->init();
//   sphere->matrix->scale(2.0f, 2.0f, 2.0f);
//   sphere->matrix->rotateY(-rot2);
//   sphere->matrix->move(3.0f, 0, 0);
//   sphere->matrix->rotateY(rot2);

//   printf("loaded4\n");
//   void* frameBuffer = canvas->render();

//   printf("rendered\n");
//   int abs_pos_x = 100;
//   int abs_pos_y = 100;

//   int x = 5;
//   int y = 80;
//   int w = 640;
//   int h = 480;
  
//   IOWR(VGA_BLT, BLT_M_RST_FIFO, 0);

//   //form3D->drawImgWin(p, x, y, w, h);
//   IOWR(VGA_BLT, BLT_M_RST_FIFO, 0);
//   IOWR(VGA_BLT, BLT_S_SRC_ADDR, (int)frameBuffer);
//   IOWR(VGA_BLT, BLT_S_DESTX, abs_pos_x+x);
//   IOWR(VGA_BLT, BLT_S_WIDTH_HEIGHT, w); // w
//   IOWR(VGA_BLT, BLT_S_WIDTH_HEIGHT, h); // h
//   IOWR(VGA_BLT, BLT_M_DESTY_MEMCPY_REQ, abs_pos_y+y);

//   printf("finish");
//   while(1);
//    return JS_UNDEFINED;
//  }









      // int width;
      // JS_ToInt32(ctx, &width, argv[0]);
      // int height;
      // JS_ToInt32(ctx, &height, argv[1]);
      // ObjData* p = new ObjData(width, height);
      // JSValue obj = JS_NewObjectClass(ctx, js_canvas3d_class_id);
      // p->js_obj = obj;
      // JS_SetOpaque(obj, p);



      // static void* loadFile(char* file){
      //   FRESULT res;
      //   FILINFO fno;
      //   res = f_stat(file, &fno);
      //   if (res != FR_OK) {
      //     printf("load error\n");
      //     while(1);
      //     return 0;
      //   }
      //   printf("size: %d\n", fno.fsize);
      //   void* p = malloc(fno.fsize);
      //   FIL fil;
      //   res = f_open(&fil, file, FA_READ);//TODO write
      //   unsigned int byteRead;
      //   res = f_read(&fil, p, fno.fsize, &byteRead);
      //   f_close(&fil);
      //   return p;
      // }
    


    //   void* cubeDat   = loadFile("/3dobj/cube.b3o"  );
    // void* aaaDat    = loadFile("/3dobj/aaa.b3o"   );
    // void* sphereDat = loadFile("/3dobj/sphere.b3o");

    // printf("loaded1\n");
    // int ww;
    // int hh;
    // void* cubeTex   = loadImg("/3dobj/cube.jpg"  , &ww, &hh);
    // void* aaaTex    = loadImg("/3dobj/aaa.jpg"   , &ww, &hh);
    // void* sphereTex = loadImg("/3dobj/sphere.jpg", &ww, &hh);  

    // printf("loaded2\n");

    // ObjData* cube   = new ObjData();
    // ObjData* aaa    = new ObjData();
    // ObjData* sphere = new ObjData();

    // cube  ->load(cubeDat  );
    // aaa   ->load(aaaDat   );
    // sphere->load(sphereDat);

    // setTexture(cube  , cubeTex  );
    // setTexture(aaa   , aaaTex   );
    // setTexture(sphere, sphereTex);
    
    // cube  ->show = true;
    // aaa   ->show = true;
    // sphere->show = true;


      //return obj;

      


//     int abs_pos_x = 100;
//     int abs_pos_y = 100;
  
//     int x = 0;
//     int y = 0;
//     int w = 640;
//     int h = 480;
    
//     IOWR(VGA_BLT, BLT_M_RST_FIFO, 0);

//     //form3D->drawImgWin(p, x, y, w, h);
//     IOWR(VGA_BLT, BLT_M_RST_FIFO, 0);
//     IOWR(VGA_BLT, BLT_S_SRC_ADDR, (int)frameBuffer);
//     IOWR(VGA_BLT, BLT_S_DESTX, abs_pos_x+x);
//     IOWR(VGA_BLT, BLT_S_WIDTH_HEIGHT, w); // w
//     IOWR(VGA_BLT, BLT_S_WIDTH_HEIGHT, h); // h
//     IOWR(VGA_BLT, BLT_M_DESTY_MEMCPY_REQ, abs_pos_y+y);

// printf("frameBuffer: %d\n", (int)frameBuffer);
// while(1);


    //void* frameBuffer = canvas3d->frameBuffer;
